Add support for Evo2 predict using a LoRA#1065
Conversation
c91aef2 to
d98c7d4
Compare
|
/ok to test d98c7d4 |
WalkthroughAdds optional LoRA support to prediction via a new CLI/API flag and model_transform passed to HyenaPredictor, introduces HyenaPredictor.configure_model, centralizes PredictionWriter into Trainer callbacks, extends training CLI with LoRA hyperparameters, adds a predict_cmd test helper and a duplicated PEFT comparison test. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant U as User/CLI
participant PA as parse_args
participant P as predict()
participant L as Evo2LoRA (optional)
participant H as HyenaPredictor
participant T as Trainer
participant W as PredictionWriter
U->>PA: predict_evo2 --lora-checkpoint-path=...
PA->>P: args (incl. lora_checkpoint_path)
P->>W: create PredictionWriter -> callbacks
alt lora_checkpoint_path provided
P->>L: load Evo2LoRA from checkpoint
P->>H: instantiate HyenaPredictor(model_transform=Evo2LoRA)
else no lora
P->>H: instantiate HyenaPredictor(no transform)
end
P->>T: start Trainer with callbacks
T->>H: run prediction
T->>W: write predictions via callback
sequenceDiagram
autonumber
participant U as User/CLI
participant PA as train parse_args
participant TR as train()
participant EL as Evo2LoRA
U->>PA: train_evo2 --lora-finetune --lora-alpha X --lora-dim Y
PA->>TR: args (alpha, dim)
TR->>TR: lora_kwargs = {alpha?: X, dim?: Y}
TR->>EL: Evo2LoRA(peft_ckpt_path=args.lora_checkpoint_path, **lora_kwargs)
EL-->>TR: lora_transform applied during model build
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (4)
🧰 Additional context used🧬 Code graph analysis (3)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (3)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (1)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (3)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
/ok to test 12a3919 |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (3)
155-157: Fix CP all-gather: wrong process group used (will hang or produce wrong logits)._gather_along_cp_dim() computes CP world size but gathers on the TP group.
Apply:
- torch.distributed.all_gather_into_tensor( - output, input_.contiguous(), group=parallel_state.get_tensor_model_parallel_group() - ) + torch.distributed.all_gather_into_tensor( + output, input_.contiguous(), group=parallel_state.get_context_parallel_group() + )
352-356: Handle output_dir=None from CLI (current code will crash).parse_args defaults output-dir to None but predict() unconditionally calls mkdir on it.
Apply:
if work_dir is None: work_dir = Path(tempfile.mkdtemp()) sequence_parallel = tensor_parallel_size > 1 and not no_sequence_parallel - output_dir.mkdir(parents=True, exist_ok=True) # Make sure the output directory exists, files will be written here. + if output_dir is None: + output_dir = work_dir / "predictions" + output_dir.mkdir(parents=True, exist_ok=True)Alternatively, make --output-dir required in parse_args.
595-633: MambaPredictor predict_step: align outputs with Hyena and return seq_idx; current log-prob logic is offset and non-vectorized.
- Compute next-token log-probs like Hyena (drop first logit, gather by input_ids[:,1:], mask).
- Include seq_idx for downstream reconstruction.
- Avoid accumulating self.predictions across steps in the returned payload to prevent duplication.
- def predict_step(self, batch, batch_idx: int, dataloader_idx: int = 0) -> dict: - """Prediction step, saving the results.""" - # Get the tokens and attention mask - if batch == {}: - batch = {"tokens": [], "position_ids": [], "attention_mask": []} - inference_data = {"tokens": batch["tokens"], "position_ids": batch["position_ids"]} - - if self.trainer.strategy._cp_size > 1: - inference_data = get_batch_on_this_context_parallel_rank( - inference_data, - num_micro_batches=1, - micro_batch_idx=0, - sequence_parallel=False, - micro_batch_size_per_context_rank=None, - context_parallel_size=self.trainer.strategy._cp_size, - sequence_length=self.config.seq_length, - multiple_of=1, - ) - inference_data["attention_mask"] = None - - with nullcontext() if torch.is_inference_mode_enabled() else torch.no_grad(): - output = self( - input_ids=inference_data["tokens"], - position_ids=inference_data["position_ids"], - inference_params=None, - attention_mask=inference_data.get("attention_mask", None), - ) - - if self.trainer.strategy._tp_size > 1: - # TP > 1 case - all-gather the output tensor along last dimension from all TP ranks for each token - # [b, s, v/p] -> [b, s, v] - output = _gather_along_last_dim(output) - - # Cache predictions - self.predictions.extend(torch.argmax(output, dim=-1).cpu().numpy().tolist()) - - # Return as dict for PredictionWriter callback - return {"predictions": self.predictions, "log_probabilities": self.log_probabilities, "tokens": self.tokens} + def predict_step(self, batch, batch_idx: int, dataloader_idx: int = 0) -> dict: + """Prediction step aligning with Hyena outputs; returns either token logits or sequence log-probs.""" + if not batch: + return {} + inference_data = { + "tokens": batch["tokens"], + "position_ids": batch["position_ids"], + "loss_mask": batch.get("loss_mask"), + "seq_idx": batch.get("seq_idx"), + } + if self.trainer.strategy._cp_size > 1: + inference_data = get_batch_on_this_context_parallel_rank( + inference_data, + num_micro_batches=1, + micro_batch_idx=0, + sequence_parallel=False, + micro_batch_size_per_context_rank=None, + context_parallel_size=self.trainer.strategy._cp_size, + sequence_length=self.config.seq_length, + multiple_of=1, + ) + inference_data["attention_mask"] = None + + with nullcontext() if torch.is_inference_mode_enabled() else torch.no_grad(): + output = self( + input_ids=inference_data["tokens"], + position_ids=inference_data["position_ids"], + inference_params=None, + attention_mask=inference_data.get("attention_mask", None), + ) + if self.trainer.strategy._tp_size > 1: + output = _gather_along_last_dim(output) + + if self.output_log_prob_seqs: + # Align predictions with next-token targets + softmax_logprobs = torch.log_softmax(output, dim=-1)[:, :-1] + input_ids = inference_data["tokens"][:, 1:] + logprobs = torch.gather(softmax_logprobs, 2, input_ids.unsqueeze(-1)).squeeze(-1) + if inference_data.get("loss_mask") is not None: + logprobs = logprobs * inference_data["loss_mask"][:, 1:].float() + if self.log_prob_collapse_option == "per_token": + return {"log_probs_seqs": logprobs.cpu(), "seq_idx": inference_data["seq_idx"].cpu()} + else: + seq_log = torch.sum(logprobs, dim=1) + if self.log_prob_collapse_option == "mean": + denom = (inference_data["loss_mask"][:, 1:].float().sum(dim=-1) + 1e-8) + seq_log = seq_log / denom + return {"log_probs_seqs": seq_log.cpu(), "seq_idx": inference_data["seq_idx"].cpu()} + else: + return { + "token_logits": output.cpu(), + "pad_mask": inference_data.get("loss_mask", None).cpu() if inference_data.get("loss_mask") is not None else None, + "seq_idx": inference_data.get("seq_idx", None).cpu() if inference_data.get("seq_idx") is not None else None, + }
🧹 Nitpick comments (12)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (4)
23-27: Deduplicate --limit-val-batches flag (appears twice).
Harmless but noisy; keep a single occurrence in each command builder.Apply this diff in both builders:
- "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern SDH* --limit-val-batches 1 " + "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern SDH* " - f"--max-steps {max_steps} --warmup-steps 1 --val-check-interval {val_check} --limit-val-batches 1 " + f"--max-steps {max_steps} --warmup-steps 1 --val-check-interval {val_check} --limit-val-batches 1 "Also applies to: 43-49
23-28: Quote wildcard argument and path-like values to avoid shell globbing and whitespace issues.
SDH*can expand in a shell; paths with spaces can break. Quote the pattern and use shlex.quote for dynamic paths.Apply this diff:
+import shlex @@ - f"train_evo2 --mock-data --result-dir {path} --devices {devices} " - "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern SDH* --limit-val-batches 1 " + f"train_evo2 --mock-data --result-dir {shlex.quote(str(path))} --devices {devices} " + "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern 'SDH*' --limit-val-batches 1 " @@ - f"train_evo2 --mock-data --result-dir {path} --devices {devices} " - "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern SDH* --limit-val-batches 1 " + f"train_evo2 --mock-data --result-dir {shlex.quote(str(path))} --devices {devices} " + "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern 'SDH*' --limit-val-batches 1 " @@ - f"predict_evo2 --fasta {fasta_file_path} --ckpt-dir {ckpt_dir} --output-dir {output_dir} " - "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern SDH* --tensor-parallel-size 1 " + f"predict_evo2 --fasta {shlex.quote(str(fasta_file_path))} --ckpt-dir {shlex.quote(str(ckpt_dir))} --output-dir {shlex.quote(str(output_dir))} " + "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern 'SDH*' --tensor-parallel-size 1 "Additional change outside hunks (add import at top of file):
+import shlexAlso applies to: 43-49, 56-59
53-55: Fix docstring typo.
“fro” → “for”.Apply:
- """Command fro predict.""" + """Command for predict."""
53-53: PEP8 spacing in signature.
Add a space around '='.-def predict_cmd(ckpt_dir: str, output_dir: str, fasta_file_path: str, additional_args: str=""): +def predict_cmd(ckpt_dir: str, output_dir: str, fasta_file_path: str, additional_args: str = ""):sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_train.py (1)
40-50: Reduce noise: print captured streams only on failure.
Always printing stdout/stderr makes CI logs noisy.- print("Captured STDOUT:\n", train_stdout) - print("Captured STDERR:\n", train_stderr) + # Consider logging on failure only, or behind a verbosity flag. + # print("Captured STDOUT:\n", train_stdout) + # print("Captured STDERR:\n", train_stderr)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_lora.py (2)
205-210: Load prediction tensors onto CPU to avoid device mismatches.
Prevents accidental CUDA dependency during test artifact inspection.- results_original = torch.load(f"{result_dir_original}/predictions__rank_0.pt") - results_peft = torch.load(f"{result_dir_peft}/predictions__rank_0.pt") + results_original = torch.load(f"{result_dir_original}/predictions__rank_0.pt", map_location="cpu") + results_peft = torch.load(f"{result_dir_peft}/predictions__rank_0.pt", map_location="cpu")
126-129: Minor: variable naming for resumed finetune output.
Rename to avoid shadowing and clarify intent.- stdout_finetune: str = run_command_in_subprocess(command=command_resume_finetune, path=str(tmp_path)) + stdout_finetune_resume: str = run_command_in_subprocess(command=command_resume_finetune, path=str(tmp_path))sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (5)
339-340: Include 'per_token' in type hint.CLI allows per_token and Hyena path handles it; signature excludes it.
- log_prob_collapse_option: Literal["sum", "mean"] = "mean", + log_prob_collapse_option: Literal["sum", "mean", "per_token"] = "mean",
380-381: Confirm tokenizer API.get_nmt_tokenizer("byte-level") assumes positional arg = library; Nemo versions sometimes require keyword library=....
If needed:
- tokenizer = get_nmt_tokenizer("byte-level") + tokenizer = get_nmt_tokenizer(library="byte-level")Also consider pulling library from model config for parity with train-time tokenizer.
432-468: Minor trainer config nits.
- limit_val_batches has no effect for predict; consider limit_predict_batches if needed.
- sequence_parallel argument repeats the TP>1 check already encoded in sequence_parallel var.
- sequence_parallel=tensor_parallel_size > 1 and sequence_parallel, + sequence_parallel=sequence_parallel, - log_every_n_steps=1, - limit_val_batches=10, + log_every_n_steps=1, + # limit_predict_batches=... # set if needed for smoke tests
134-140: CLI wiring for LoRA looks good; consider making the behavior explicit in help.E.g., note that LoRA is currently applied only for model_type="hyena".
If you adopt the warning for Mamba, no change needed.
448-454: Sampler seq_len hard-coded to 8192.Ensure this matches the active config (e.g., 1m models). Otherwise, expose as arg or derive from config.seq_length.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (7)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/peft.py(0 hunks)sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py(8 hunks)sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py(2 hunks)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/__init__.py(1 hunks)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py(1 hunks)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_lora.py(1 hunks)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_train.py(1 hunks)
💤 Files with no reviewable changes (1)
- sub-packages/bionemo-evo2/src/bionemo/evo2/run/peft.py
🧰 Additional context used
🧬 Code graph analysis (3)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_lora.py (3)
sub-packages/bionemo-testing/src/bionemo/testing/data/fasta.py (1)
create_fasta_file(28-63)sub-packages/bionemo-testing/src/bionemo/testing/subprocess_utils.py (1)
run_command_in_subprocess(108-129)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (3)
predict_cmd(53-61)small_training_cmd(20-29)small_training_finetune_cmd(32-50)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_train.py (1)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (2)
small_training_cmd(20-29)small_training_finetune_cmd(32-50)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (5)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/peft.py (1)
Evo2LoRA(26-279)sub-packages/bionemo-evo2/src/bionemo/evo2/models/mamba.py (1)
configure_model(356-383)sub-packages/bionemo-llm/src/bionemo/llm/lightning.py (1)
configure_model(307-329)sub-packages/bionemo-llm/src/bionemo/llm/utils/callbacks.py (1)
PredictionWriter(31-154)sub-packages/bionemo-evo2/src/bionemo/evo2/utils/checkpoint/convert_to_nemo.py (2)
tokenizer(137-149)config(152-158)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (rust)
🔇 Additional comments (7)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/__init__.py (1)
1-14: LGTM — package marker only.
Header-only file to enable test discovery; no action needed.sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (2)
495-496: CLI type for --lora-checkpoint-path changed to str — confirm downstream expectations.
If any consumer expects a PathLike, use os.fspath() or Path() at the usage site to avoid surprises.Apply (at the creation site) if needed:
- lora_transform = Evo2LoRA(peft_ckpt_path=args.lora_checkpoint_path) + from os import fspath + lora_transform = Evo2LoRA(peft_ckpt_path=fspath(args.lora_checkpoint_path) if args.lora_checkpoint_path else None)
629-634: Avoid double-wiring Evo2LoRA (model_transform and callback) unless required.
Passinglora_transformto bothHyenaModel(..., model_transform=...)and as a Lightning callback might duplicate effects.Please confirm Evo2LoRA’s intended integration point. If it’s a callback, drop the
model_transform=argument; if it’s a config/model transform, don’t also add it as a callback. I can propose a follow-up diff once confirmed.Also applies to: 658-659
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_train.py (2)
33-34: Good reuse of shared helpers.
Importing from common reduces duplication across tests.
53-61: Confirm intentional seq-length change between Mamba pretrain (8) and finetune (16).
If unintentional, align the values; if intentional, add a brief comment explaining the rationale.Also applies to: 64-74
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_lora.py (1)
30-114: Nice coverage — LoRA on/off matrix with resume path.
End-to-end checks for logs, checkpoints, and TB outputs look solid.sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (1)
41-43: Import Evo2LoRA.Import looks correct and localized to Hyena path only.
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_finetune.py
Outdated
Show resolved
Hide resolved
dorotat-nv
left a comment
There was a problem hiding this comment.
Added comments to #1066
12a3919 to
35dc042
Compare
|
/ok to test 35dc042 |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (5)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (1)
634-634: Type inconsistency for --lora-checkpoint-path argument.The type is changed from
Pathtostrin train.py, but it remainsPathin predict.py (line 165). This inconsistency could cause confusion.Apply this diff to maintain consistency:
- parser.add_argument("--lora-checkpoint-path", type=str, default=None, help="LoRA checkpoint path") + parser.add_argument("--lora-checkpoint-path", type=Path, default=None, help="LoRA checkpoint path")Then update line 821 to use the Path object:
- lora_transform = Evo2LoRA(peft_ckpt_path=args.lora_checkpoint_path) + lora_transform = Evo2LoRA(peft_ckpt_path=str(args.lora_checkpoint_path) if args.lora_checkpoint_path else None)sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (1)
441-450: Consider using batch writer for multi-device compatibility.The
PredictionWriteris set towrite_interval="epoch"which may cause issues whennum_devices > 1(common in multi-parallel scenarios). The batch writer is more compatible with distributed setups.Consider using batch writer as the default or make it configurable based on the parallelism settings:
callbacks = [ PredictionWriter( output_dir=output_dir, - write_interval=write_interval, + write_interval="batch" if (tensor_parallel_size * pipeline_model_parallel_size * context_parallel_size) > 1 else write_interval, batch_dim_key_defaults={"token_logits": 0}, seq_dim_key_defaults={"token_logits": 1}, files_per_subdir=files_per_subdir, save_all_model_parallel_ranks=False, # only write one copy of predictions. ) ]sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (3)
30-36: Quote glob pattern and paths to avoid shell globbing and path breakage.When executed via a shell,
SDH*may expand to filesystem matches; unquoted paths may break on spaces. QuoteSDH*and path-like args.Apply:
- f"train_evo2 --mock-data --result-dir {path} --devices {devices} " - "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern SDH* --limit-val-batches 1 " + f'train_evo2 --mock-data --result-dir "{path}" --devices {devices} ' + "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern 'SDH*' --limit-val-batches 1 " @@ - f"train_evo2 --mock-data --result-dir {path} --devices {devices} " - "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern SDH* --limit-val-batches 1 " + f'train_evo2 --mock-data --result-dir "{path}" --devices {devices} ' + "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern 'SDH*' --limit-val-batches 1 " @@ - f"--seq-length 16 --hidden-dropout 0.1 --attention-dropout 0.1 {additional_args} --ckpt-dir {prev_ckpt} " + f'--seq-length 16 --hidden-dropout 0.1 --attention-dropout 0.1 {additional_args} --ckpt-dir "{prev_ckpt}" ' @@ - f"predict_evo2 --fasta {fasta_file_path} --ckpt-dir {ckpt_dir} --output-dir {output_dir} " - "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern SDH* --tensor-parallel-size 1 " + f'predict_evo2 --fasta "{fasta_file_path}" --ckpt-dir "{ckpt_dir}" --output-dir "{output_dir}" ' + "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern 'SDH*' --tensor-parallel-size 1 "Longer-term, prefer constructing argument lists and passing to subprocess with
shell=Falseto avoid quoting concerns.Also applies to: 52-59, 66-69
64-64: Fix typo in docstring (“fro” → “for”).Apply:
- """Command fro predict.""" + """Command for predict."""
63-71: Expose LoRA checkpoint as a first-class arg in predict_cmd (use --lora-checkpoint-path).Add parameter
lora_checkpoint_path: str | None = Noneto predict_cmd and, if set, append--lora-checkpoint-path {lora_checkpoint_path}to the constructed command; keepadditional_argsfor backward-compatible flexibility. Confirmed CLI flag--lora-checkpoint-pathin sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/peft.py(0 hunks)sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py(7 hunks)sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py(4 hunks)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/__init__.py(1 hunks)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py(1 hunks)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_finetune.py(1 hunks)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_train.py(1 hunks)
💤 Files with no reviewable changes (1)
- sub-packages/bionemo-evo2/src/bionemo/evo2/run/peft.py
🚧 Files skipped from review as they are similar to previous changes (2)
- sub-packages/bionemo-evo2/tests/bionemo/evo2/run/init.py
- sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_train.py
🧰 Additional context used
🧬 Code graph analysis (2)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_finetune.py (4)
sub-packages/bionemo-testing/src/bionemo/testing/data/fasta.py (1)
create_fasta_file(28-63)sub-packages/bionemo-testing/src/bionemo/testing/subprocess_utils.py (1)
run_command_in_subprocess(108-129)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (3)
predict_cmd(63-71)small_training_cmd(20-37)small_training_finetune_cmd(40-60)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_train.py (1)
test_train_evo2_finetune_runs(103-171)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (3)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/peft.py (1)
Evo2LoRA(26-279)sub-packages/bionemo-evo2/src/bionemo/evo2/models/mamba.py (1)
configure_model(356-383)sub-packages/bionemo-llm/src/bionemo/llm/lightning.py (1)
configure_model(307-329)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (rust)
🔇 Additional comments (14)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (3)
91-110: LGTM! Mock data flags provide useful configurability.The addition of mock dataset size control parameters allows flexible testing scenarios. Default values are reasonable for most use cases.
692-694: LGTM! Mock data module properly uses new parameters.The mock data module is correctly instantiated with the new size parameters.
848-849: Potential NameError when --lora-finetune is used with Mamba.
lora_transformis only defined in the Hyena branch (line 821), but it's appended to callbacks unconditionally at line 849, which will cause a NameError for Mamba models.Apply this diff to fix the issue:
+ lora_transform = None # Create model based on selected model type if model_type == "hyena": if args.model_size not in HYENA_MODEL_OPTIONS: raise ValueError(f"Invalid model size for Hyena: {args.model_size}") model_config = HYENA_MODEL_OPTIONS[args.model_size](**config_modifiers_init) if args.no_weight_decay_embeddings: # Override the default weight decay condition for Hyena with our bionemo version that also excludes # embeddings model_config.hyena_no_weight_decay_cond_fn = hyena_no_weight_decay_cond_with_embeddings # Lora adaptors configuration - lora_transform = None if args.lora_finetune: lora_transform = Evo2LoRA(peft_ckpt_path=args.lora_checkpoint_path) model = llm.HyenaModel(model_config, tokenizer=data_module.tokenizer, model_transform=lora_transform) elif model_type == "mamba": # mambaAnd update the callback append logic:
- if args.lora_finetune: - callbacks.append(lora_transform) + if lora_transform is not None: + callbacks.append(lora_transform)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_finetune.py (4)
32-52: LGTM! Clean validation loss extraction utility.The function provides a clean way to extract and sample validation losses from logs for monitoring training progress.
106-110: LGTM! Good validation of monotonic loss decrease.This test ensures that the model is learning properly by checking that validation losses decrease monotonically at sampled points.
285-288: LGTM! Comprehensive LoRA finetune verification.Good test coverage confirming that LoRA finetuning produces different outputs from the base model while maintaining consistent sequence indices and shapes.
272-272: Bug: assertion checks the wrong variable for restore message validation.The assertion checks
stdout_finetuneinstead ofstdout_predict, so the prediction restore path isn't actually tested.Apply this diff:
- assert "Restoring model weights from RestoreConfig(path='" in stdout_finetune + assert "Restoring model weights from RestoreConfig(path='" in stdout_predictsub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (5)
163-169: LGTM! LoRA support properly added to CLI.The command-line argument for LoRA checkpoint path is properly integrated.
482-494: LGTM! LoRA properly integrated for Hyena models.The LoRA transformation is correctly instantiated and passed to the HyenaPredictor when a LoRA checkpoint path is provided.
505-510: Warn when LoRA path is provided for Mamba models.If a user provides a LoRA checkpoint path with a Mamba model, it will be silently ignored, which could lead to confusion.
Add a warning when LoRA is requested but not supported:
else: # mamba if model_size not in MAMBA_MODEL_OPTIONS: raise ValueError(f"Invalid model size for Mamba: {model_size}") + if lora_checkpoint_path: + logger.warning("LoRA checkpoint provided but LoRA is not supported for Mamba models; ignoring.") config = MAMBA_MODEL_OPTIONS[model_size](
278-280: LGTM! MambaPredictor class properly structured.The MambaPredictor class correctly inherits from both BasePredictor and MambaModel for prediction functionality.
272-275: Guard trainer access in configure_model to prevent AttributeError.If
configure_modelis called before the Trainer is attached, accessingself.trainer.strategywill raise an AttributeError.Apply this diff to safely handle trainer access:
def configure_model(self, *args, **kwargs) -> None: """Configure the model.""" super().configure_model(*args, **kwargs) - self.trainer.strategy._init_model_parallel = True + if hasattr(self, "trainer") and self.trainer is not None: + if hasattr(self.trainer, "strategy"): + self.trainer.strategy._init_model_parallel = Truesub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (2)
24-26: No change required — project targets Python ≥3.10pyproject.toml declares requires-python = ">=3.10" and CI workflows run on Python 3.11/3.13, so
int | Noneis supported.
4-5: SPDX header: add missing period; keep LicenseRef-Apache2File: sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (lines 4–5)
- Add a trailing period to line 4.
- Do not change line 5 — the repository uses LicenseRef-Apache2.
Suggested fix:
-# SPDX-FileCopyrightText: Copyright (c) 2024 Stanford University. All rights reserved +# SPDX-FileCopyrightText: Copyright (c) 2024 Stanford University. All rights reserved. # SPDX-License-Identifier: LicenseRef-Apache2Likely an incorrect or invalid review comment.
3f40581 to
f3ac5b0
Compare
f3ac5b0 to
41f9092
Compare
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_finetune.py
Outdated
Show resolved
Hide resolved
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_finetune.py
Outdated
Show resolved
Hide resolved
08e2453 to
5c25dea
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (1)
63-71: Fix docstring typo and keep style consistent.Minor typo and spacing nit in signature.
-def predict_cmd(ckpt_dir: str, output_dir: str, fasta_file_path: str, additional_args: str = ""): - """Command fro predict.""" +def predict_cmd(ckpt_dir: str, output_dir: str, fasta_file_path: str, additional_args: str = ""): + """Command for predict."""sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (1)
441-451: PredictionWriter mappings incomplete for log_probs_seqs; prefer explicit dims.When output_log_prob_seqs=True, writer needs dims for log_probs_seqs. Also future keys like predictions benefit from defaults.
callbacks = [ PredictionWriter( output_dir=output_dir, write_interval=write_interval, - batch_dim_key_defaults={"token_logits": 0}, - seq_dim_key_defaults={"token_logits": 1}, + batch_dim_key_defaults={"token_logits": 0, "log_probs_seqs": 0, "predictions": 0}, + seq_dim_key_defaults={"token_logits": 1, "log_probs_seqs": 1}, files_per_subdir=files_per_subdir, save_all_model_parallel_ranks=False, # only write one copy of predictions. ) ]
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py(7 hunks)sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py(2 hunks)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py(1 hunks)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py
🧰 Additional context used
🧬 Code graph analysis (2)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (3)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (2)
predict_cmd(63-71)small_training_finetune_cmd(40-60)sub-packages/bionemo-testing/src/bionemo/testing/subprocess_utils.py (1)
run_command_in_subprocess(108-129)sub-packages/bionemo-testing/src/bionemo/testing/data/fasta.py (1)
create_fasta_file(28-63)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (2)
sub-packages/bionemo-evo2/src/bionemo/evo2/models/peft.py (1)
Evo2LoRA(26-279)sub-packages/bionemo-llm/src/bionemo/llm/lightning.py (1)
configure_model(307-329)
🪛 GitHub Actions: BioNeMo Framework CI
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py
[error] 390-390: F821: Undefined name 'small_training_finetune_cmd'
[error] 398-398: F821: Undefined name 'run_command_in_subprocess'
[error] 412-412: F821: Undefined name 'run_command_in_subprocess'
[error] 389-392: F821: Undefined name 'small_training_finetune_cmd'
[error] 396-396: F821: Undefined name 'run_command_in_subprocess'
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: verify-tests-status
- GitHub Check: Analyze (rust)
🔇 Additional comments (10)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (6)
44-44: LoRA import addition looks correct.
163-169: CLI: LoRA path flag LGTM.Flag name, type, and help are consistent with usage below.
482-487: LoRA integration path (Hyena) looks correct.Transform injected via callback and model_transform; aligns with Evo2LoRA behavior.
Please confirm Evo2LoRA implements the Callback API points used during predict (setup/on_predict_epoch_start) so the transform applies before weights are loaded.
495-511: Warn or error if LoRA path provided for Mamba (currently ignored).Avoid silent ignore; emit a clear warning (or raise).
else: # mamba if model_size not in MAMBA_MODEL_OPTIONS: raise ValueError(f"Invalid model size for Mamba: {model_size}") + if lora_checkpoint_path: + logger.warning( + f"LoRA checkpoint provided ({lora_checkpoint_path}) but LoRA is not applied for Mamba models; ignoring." + ) config = MAMBA_MODEL_OPTIONS[model_size]( forward_step_fn=hyena_predict_forward_step, # Can reuse the same forward steps data_step_fn=hyena_predict_data_step, distribute_saved_activations=False if sequence_parallel and tensor_parallel_size > 1 else True, **config_modifiers_init, )
599-600: main forwards LoRA path correctly.
272-276: Guard trainer access in configure_model to avoid AttributeError.self.trainer may be None here.
def configure_model(self, *args, **kwargs) -> None: """Configure the model.""" super().configure_model(*args, **kwargs) - self.trainer.strategy._init_model_parallel = True + trainer = getattr(self, "trainer", None) + if trainer is not None and getattr(trainer, "strategy", None) is not None: + trainer.strategy._init_model_parallel = Truesub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (2)
63-71: Avoid shadowing the top-level package by relocating test helper module.Placing test helpers under tests/bionemo/... can shadow the real bionemo package during test runs. Move this file to a tests-only namespace (e.g., tests/helpers/evo2/common.py) and update imports.
63-71: Confirm ‘1b_nv’ is supported in HYENA_MODEL_OPTIONS
Ensure the predict helper’s default--model-size 1b_nvis included in the imported NemoHYENA_MODEL_OPTIONS; manual upstream verification required.sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (2)
433-434: Bug: assert checks wrong stdout variable after PEFT predict.Should assert against stdout_predict.
- assert "Restoring model weights from RestoreConfig(path='" in stdout_finetune + assert "Restoring model weights from RestoreConfig(path='" in stdout_predict
36-37: Pipeline blocker: missing imports (F821).Tests reference small_training_finetune_cmd and run_command_in_subprocess but don’t import them.
-from .common import predict_cmd +from .common import predict_cmd, small_training_finetune_cmd +from bionemo.testing.subprocess_utils import run_command_in_subprocess
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (1)
848-850: Guard callback append when LoRA transform is unavailable
lora_transformis only defined inside the Hyena branch. For other model types (e.g.,mamba) a--lora-finetunerun will reach this block and crash withNameError. Initialize the variable before the branch and append only when it was actually created.- if args.lora_finetune: - callbacks.append(lora_transform) + if lora_transform is not None: + callbacks.append(lora_transform)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (1)
32-38: Import the helpers you just used
small_training_finetune_cmdandrun_command_in_subprocessare referenced later but never imported, leading to the CI F821 failures. Pull them in alongsidepredict_cmd.-from .common import predict_cmd +from .common import predict_cmd, small_training_finetune_cmd +from bionemo.testing.subprocess_utils import run_command_in_subprocess
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py(7 hunks)sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py(2 hunks)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py(1 hunks)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (1)
sub-packages/bionemo-evo2/src/bionemo/evo2/models/peft.py (1)
Evo2LoRA(26-279)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (3)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (2)
predict_cmd(63-71)small_training_finetune_cmd(40-60)sub-packages/bionemo-testing/src/bionemo/testing/subprocess_utils.py (1)
run_command_in_subprocess(108-129)sub-packages/bionemo-testing/src/bionemo/testing/data/fasta.py (1)
create_fasta_file(28-63)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (3)
sub-packages/bionemo-evo2/src/bionemo/evo2/models/peft.py (1)
Evo2LoRA(26-279)sub-packages/bionemo-llm/src/bionemo/llm/lightning.py (1)
configure_model(307-329)sub-packages/bionemo-llm/src/bionemo/llm/utils/callbacks.py (1)
PredictionWriter(38-225)
🪛 GitHub Actions: BioNeMo Framework CI
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py
[error] 389-392: F821 Undefined name 'small_training_finetune_cmd'.
[error] 396-399: F821 Undefined name 'run_command_in_subprocess'.
[error] 410-413: F821 Undefined name 'run_command_in_subprocess'.
[error] 432-433: F821 Undefined name 'run_command_in_subprocess'.
🔇 Additional comments (5)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (2)
272-275: Guard trainer access inconfigure_model
configure_modelcan be invoked before the Trainer is attached, soself.trainermay beNone. The unconditional attribute access will raise, re-introducing the issue flagged earlier. Guard the access as previously suggested.def configure_model(self, *args, **kwargs) -> None: """Configure the model.""" super().configure_model(*args, **kwargs) - self.trainer.strategy._init_model_parallel = True + trainer = getattr(self, "trainer", None) + if trainer is not None and getattr(trainer, "strategy", None) is not None: + trainer.strategy._init_model_parallel = True
505-510: Fail fast when LoRA is requested for MambaSupplying
--lora-checkpoint-pathwithmodel_type="mamba"is silently ignored, so predictions run without the expected adapters. Raise (or at least warn) so users know the configuration is unsupported instead of getting wrong results.else: # mamba if model_size not in MAMBA_MODEL_OPTIONS: raise ValueError(f"Invalid model size for Mamba: {model_size}") + if lora_checkpoint_path: + raise ValueError("LoRA checkpoints are not supported for Mamba models.") config = MAMBA_MODEL_OPTIONS[model_size](sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (1)
433-434: Check the right stdout in the PEFT assertThis assert is still inspecting
stdout_finetune, so the PEFT run output never gets validated and the test will miss regressions. Point it atstdout_predict.- assert "Restoring model weights from RestoreConfig(path='" in stdout_finetune + assert "Restoring model weights from RestoreConfig(path='" in stdout_predictsub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (2)
63-71: Relocate test helper out of thebionemonamespaceAdding more helpers under
tests/bionemo/...keeps creating a shadow package that can mask the realbionemomodules during test runs. This has already caused brittle imports whensys.pathputs the tests directory ahead ofsrc/, and extending it here increases the odds of hitting that collision as soon as more predict tests land. Please move this helper to a tests-only namespace (e.g.,tests/helpers/evo2/common.py) and update the imports accordingly so we no longer install a faux top-levelbionemopackage in the test tree.
64-64: Fix docstring typoThe docstring still reads “Command fro predict.” Please update it to “Command for predict.”
5c25dea to
2829015
Compare
|
/ok to test 2829015 |
5db2c32 to
c7d8b34
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (1)
63-71: Optionally quote paths/args to be robust to spaces.Use shlex.quote on paths and the extra args when building the string.
-def predict_cmd(ckpt_dir: str, output_dir: str, fasta_file_path: str, additional_args: str = ""): +def predict_cmd(ckpt_dir: str, output_dir: str, fasta_file_path: str, additional_args: str = ""): """Command for predict.""" - cmd = ( - f"predict_evo2 --fasta {fasta_file_path} --ckpt-dir {ckpt_dir} --output-dir {output_dir} " + cmd = ( + f"predict_evo2 --fasta {shlex.quote(str(fasta_file_path))} " + f"--ckpt-dir {shlex.quote(str(ckpt_dir))} --output-dir {shlex.quote(str(output_dir))} " "--model-size 1b_nv --num-layers 4 --hybrid-override-pattern SDH* --tensor-parallel-size 1 " - f"--pipeline-model-parallel-size 1 --context-parallel-size 1 {additional_args}" + f"--pipeline-model-parallel-size 1 --context-parallel-size 1 {additional_args}" )Add at top of file (outside diff range):
import shlexsub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (1)
372-453: Make “different logits” assertion numerically robust.Use allclose with a small tolerance instead of elementwise inequality to avoid false positives/negatives.
- logits_original = results_original["token_logits"] - logits_peft = results_peft["token_logits"] - assert (logits_original != logits_peft).any() + logits_original = results_original["token_logits"] + logits_peft = results_peft["token_logits"] + # Expect a meaningful delta; fail if tensors are (near-)identical + assert not torch.allclose(logits_original, logits_peft, rtol=1e-5, atol=1e-6)sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (2)
441-450: PredictionWriter config is incomplete for log-prob outputs; handle multi-device epoch writes.
- Include dims for log_probs_seqs to ensure correct collation.
- Optionally force write_interval="batch" when devices>1 to avoid epoch write limitations.
- callbacks = [ + # Normalize writer interval in multi-device runs + if (devices or 1) > 1 and write_interval == "epoch": + logger.warning("write_interval='epoch' with multi-device is unsupported; falling back to 'batch'.") + write_interval = "batch" + + callbacks = [ PredictionWriter( output_dir=output_dir, write_interval=write_interval, - batch_dim_key_defaults={"token_logits": 0}, - seq_dim_key_defaults={"token_logits": 1}, + batch_dim_key_defaults={"token_logits": 0, "log_probs_seqs": 0, "predictions": 0}, + seq_dim_key_defaults={"token_logits": 1, "log_probs_seqs": 1}, files_per_subdir=files_per_subdir, save_all_model_parallel_ranks=False, # only write one copy of predictions. ) ]
496-511: Explicitly handle LoRA path for Mamba models.Warn or raise if a LoRA checkpoint is provided since it’s not applied for Mamba.
else: # mamba + if lora_checkpoint_path: + logger.warning( + f"LoRA checkpoint provided ({lora_checkpoint_path}) but LoRA is not applied for Mamba models; ignoring." + ) if model_size not in MAMBA_MODEL_OPTIONS: raise ValueError(f"Invalid model size for Mamba: {model_size}")
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py(7 hunks)sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py(2 hunks)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py(1 hunks)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py
🧰 Additional context used
🧬 Code graph analysis (2)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (3)
sub-packages/bionemo-testing/src/bionemo/testing/subprocess_utils.py (1)
run_command_in_subprocess(108-129)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (2)
predict_cmd(63-71)small_training_finetune_cmd(40-60)sub-packages/bionemo-testing/src/bionemo/testing/data/fasta.py (1)
create_fasta_file(28-63)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (2)
sub-packages/bionemo-evo2/src/bionemo/evo2/models/peft.py (1)
Evo2LoRA(26-279)sub-packages/bionemo-llm/src/bionemo/llm/lightning.py (1)
configure_model(307-329)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (rust)
🔇 Additional comments (5)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (2)
1-1: Move test helper out of the library namespace to avoid shadowingbionemo.Place this under a tests-only path (e.g., tests/helpers/evo2/common.py) and update imports. Keeping tests/bionemo/... risks overshadowing the real package during test runs.
63-71: Fix docstring typo (“fro” → “for”).Also safe to keep signature spacing consistent.
-def predict_cmd(ckpt_dir: str, output_dir: str, fasta_file_path: str, additional_args: str = ""): - """Command fro predict.""" +def predict_cmd(ckpt_dir: str, output_dir: str, fasta_file_path: str, additional_args: str = ""): + """Command for predict."""sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (2)
34-34: LGTM: missing import fixed.
37-37: LGTM: helper import added.sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (1)
272-276: Guard trainer access in configure_model to avoid AttributeError.self.trainer may be None when this runs.
def configure_model(self, *args, **kwargs) -> None: """Configure the model.""" super().configure_model(*args, **kwargs) - self.trainer.strategy._init_model_parallel = True + trainer = getattr(self, "trainer", None) + if trainer is not None and getattr(trainer, "strategy", None) is not None: + trainer.strategy._init_model_parallel = True
|
/ok to test 55a085e |
55a085e to
f1df1d7
Compare
|
/ok to test f1df1d7 |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (3)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (2)
386-386: Reduce flakiness: bump finetune steps slightly.Two steps may sometimes be too little to produce observable differences. Consider 4.
Apply this diff:
-num_steps = 2 +num_steps = 4
420-433: Make checkpoint discovery robust across naming schemes.Matching by suffix "{steps}.0-last" is brittle. Prefer the most recent subdir by mtime.
Apply this diff:
- expected_checkpoint_suffix = f"{num_steps}.0-last" - # Check if any subfolder ends with the expected suffix - matching_subfolders = [ - p for p in checkpoints_dir.iterdir() if p.is_dir() and (expected_checkpoint_suffix in p.name) - ] - - assert matching_subfolders, ( - f"No checkpoint subfolder ending with '{expected_checkpoint_suffix}' found in {checkpoints_dir}." - ) - - result_dir_peft = tmp_path / "results_peft" - additional_args = f"--lora-checkpoint-path {matching_subfolders[0]}" + # Pick the most recent checkpoint subfolder (portable across backends) + subdirs = [p for p in checkpoints_dir.iterdir() if p.is_dir()] + assert subdirs, f"No checkpoint subfolders found in {checkpoints_dir}." + lora_ckpt_dir = max(subdirs, key=lambda p: p.stat().st_mtime) + + result_dir_peft = tmp_path / "results_peft" + additional_args = f"--lora-checkpoint-path {lora_ckpt_dir}"sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (1)
441-450: Include mappings for log_probs_seqs in PredictionWriter.Ensures per-token mode and future outputs write correctly without relying on implicit defaults.
Apply this diff:
callbacks = [ PredictionWriter( output_dir=output_dir, write_interval=write_interval, - batch_dim_key_defaults={"token_logits": 0}, - seq_dim_key_defaults={"token_logits": 1}, + batch_dim_key_defaults={"token_logits": 0, "log_probs_seqs": 0}, + seq_dim_key_defaults={"token_logits": 1, "log_probs_seqs": 1}, files_per_subdir=files_per_subdir, save_all_model_parallel_ranks=False, # only write one copy of predictions. ) ]
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py(7 hunks)sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py(2 hunks)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py(1 hunks)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py
🧰 Additional context used
🧬 Code graph analysis (2)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (3)
sub-packages/bionemo-testing/src/bionemo/testing/subprocess_utils.py (1)
run_command_in_subprocess(108-129)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (2)
predict_cmd(63-71)small_training_finetune_cmd(40-60)sub-packages/bionemo-testing/src/bionemo/testing/data/fasta.py (1)
create_fasta_file(28-63)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (3)
sub-packages/bionemo-evo2/src/bionemo/evo2/models/peft.py (1)
Evo2LoRA(26-279)sub-packages/bionemo-llm/src/bionemo/llm/lightning.py (1)
configure_model(307-329)sub-packages/bionemo-llm/src/bionemo/llm/utils/callbacks.py (1)
PredictionWriter(38-225)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: pre-commit
- GitHub Check: Analyze (rust)
🔇 Additional comments (6)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (2)
63-71: Fix docstring typo in predict_cmd.Change "fro" to "for".
Apply this diff:
def predict_cmd(ckpt_dir: str, output_dir: str, fasta_file_path: str, additional_args: str = ""): - """Command fro predict.""" + """Command for predict."""
63-71: Avoid tests creating a top-level bionemo package (module shadowing).This helper under tests/bionemo/... can shadow the real bionemo package during test runs. Move it to a tests-only namespace (e.g., tests/helpers/evo2/common.py) and update imports.
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (2)
34-38: LGTM: required imports added.Adds missing utilities; resolves prior F821 failures.
434-436: Consider relaxing the log assertions to avoid brittleness.Exact substrings can change with upstream logging. If this flakes, drop or soften these checks; the logits inequality already validates LoRA was applied.
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (2)
272-276: Guard trainer access in HyenaPredictor.configure_model.Accessing self.trainer.strategy unguarded can raise if called before Trainer attach.
Apply this diff:
def configure_model(self, *args, **kwargs) -> None: """Configure the model.""" super().configure_model(*args, **kwargs) - self.trainer.strategy._init_model_parallel = True + trainer = getattr(self, "trainer", None) + if trainer is not None and getattr(trainer, "strategy", None) is not None: + trainer.strategy._init_model_parallel = True
495-511: Warn when LoRA path is provided for Mamba models (unsupported).Currently ignored silently; emit a clear warning for user visibility.
Apply this diff:
else: # mamba - if model_size not in MAMBA_MODEL_OPTIONS: + if model_size not in MAMBA_MODEL_OPTIONS: raise ValueError(f"Invalid model size for Mamba: {model_size}") + if lora_checkpoint_path: + logger.warning( + f"LoRA checkpoint provided ({lora_checkpoint_path}) but LoRA is not applied for Mamba models; ignoring." + ) config = MAMBA_MODEL_OPTIONS[model_size]( forward_step_fn=hyena_predict_forward_step, # Can reuse the same forward steps data_step_fn=hyena_predict_data_step, distribute_saved_activations=False if sequence_parallel and tensor_parallel_size > 1 else True, **config_modifiers_init, )
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1065 +/- ##
==========================================
- Coverage 79.76% 79.75% -0.02%
==========================================
Files 156 156
Lines 11616 11628 +12
==========================================
+ Hits 9266 9274 +8
- Misses 2350 2354 +4
|
f1df1d7 to
014de80
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (1)
852-853: NameError persists when --lora-finetune is used with Mamba/Llama.
lora_transformis only defined in the Hyena branch (line 814), but line 853 attempts to append it for any model type when--lora-finetuneis set. This will raiseNameErrorif a user combines--lora-finetunewith a Mamba or Llama model.Apply this diff to guard the callback append:
- if args.lora_finetune: - callbacks.append(lora_transform) + if args.lora_finetune: + if model_type == "hyena": + callbacks.append(lora_transform) + else: + raise ValueError(f"LoRA fine-tuning is only supported for Hyena models, not {model_type}")Alternatively, initialize
lora_transform = Nonebefore the model type branching (line 804) and only append when not None:+ lora_transform = None + # Create model based on selected model type if model_type == "hyena": ... - lora_transform = None ... - if args.lora_finetune: + if lora_transform is not None: callbacks.append(lora_transform)sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (1)
495-510: Mamba silently ignores LoRA checkpoint path.If
lora_checkpoint_pathis provided butmodel_type="mamba", the LoRA checkpoint is silently ignored. This can lead to user confusion when predictions don't reflect the expected fine-tuned behavior.Add an explicit check before creating the Mamba model:
else: # mamba + if lora_checkpoint_path: + raise ValueError( + f"LoRA checkpoints are not supported for Mamba models. " + f"Provided checkpoint path: {lora_checkpoint_path}" + ) + if model_size not in MAMBA_MODEL_OPTIONS:Or emit a warning if raising is too strict:
else: # mamba + if lora_checkpoint_path: + logger.warning( + f"LoRA checkpoint path provided ({lora_checkpoint_path}) but LoRA is not supported " + f"for Mamba models. The checkpoint will be ignored." + ) + if model_size not in MAMBA_MODEL_OPTIONS:
🧹 Nitpick comments (4)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (1)
639-650: Clarify LoRA parameter descriptions in help text.The help text for
--lora-alphaand--lora-dimis generic. Consider improving clarity:
--lora-alpha: "LoRA scaling parameter (alpha)" instead of "Alpha parameter for LoRA fine-tuning."--lora-dim: "LoRA rank (dimension)" instead of "Dim parameter for LoRA fine-tuning."Apply this diff to improve the help text:
parser.add_argument( "--lora-alpha", type=int, default=None, - help="Alpha parameter for LoRA fine-tuning.", + help="LoRA scaling parameter (alpha).", ) parser.add_argument( "--lora-dim", type=int, default=None, - help="Dim parameter for LoRA fine-tuning.", + help="LoRA rank (dimension).", )sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (3)
163-169: Clarify help text for --lora-checkpoint-path.Consider improving the help text for clarity:
Apply this diff:
ap.add_argument( "--lora-checkpoint-path", type=Path, required=False, default=None, - help="Path to the lora states to restore from.", + help="Path to the LoRA adapter checkpoint directory.", )
441-450: Document multi-device limitation with epoch write interval.The
write_intervalparameter is now configurable, butPredictionWriterwill raise an error ifwrite_interval="epoch"is used withnum_devices > 1. Consider adding validation or documentation.Add validation in the
predictfunction before the callbacks list:+ if write_interval == "epoch" and devices > 1: + raise ValueError( + "write_interval='epoch' is not supported with multiple devices. " + "Please use write_interval='batch' instead." + ) + callbacks = [ PredictionWriter(Alternatively, document this limitation in the CLI help text:
ap.add_argument( "--write-interval", type=str, default="epoch", choices=["epoch", "batch"], - help="Interval to write predictions to disk. If doing very large predictions, you may want to set this to 'batch'.", + help="Interval to write predictions to disk. Use 'batch' when using multiple devices. If doing very large predictions, you may want to set this to 'batch'.", )
445-446: Add dimension defaults for all output keys.The
PredictionWriteronly specifies dimension defaults fortoken_logits, but the model can also outputlog_probs_seqs(whenoutput_log_prob_seqs=True) and potentially other keys. Missing dimension defaults may cause issues during batch collation.Based on the model's output in
predict_step(lines 250-266), add dimension defaults for all keys:PredictionWriter( output_dir=output_dir, write_interval=write_interval, - batch_dim_key_defaults={"token_logits": 0}, - seq_dim_key_defaults={"token_logits": 1}, + batch_dim_key_defaults={"token_logits": 0, "log_probs_seqs": 0, "pad_mask": 0, "seq_idx": 0}, + seq_dim_key_defaults={"token_logits": 1, "log_probs_seqs": 1, "pad_mask": 1}, files_per_subdir=files_per_subdir,
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py(7 hunks)sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py(2 hunks)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py(1 hunks)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py
🧰 Additional context used
🧬 Code graph analysis (2)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (1)
sub-packages/bionemo-evo2/src/bionemo/evo2/models/peft.py (1)
Evo2LoRA(26-279)
sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (3)
sub-packages/bionemo-testing/src/bionemo/testing/subprocess_utils.py (1)
run_command_in_subprocess(108-129)sub-packages/bionemo-evo2/tests/bionemo/evo2/run/common.py (2)
predict_cmd(63-71)small_training_finetune_cmd(40-60)sub-packages/bionemo-testing/src/bionemo/testing/data/fasta.py (1)
create_fasta_file(28-63)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: changed-files
- GitHub Check: pre-commit
- GitHub Check: changed-dirs
- GitHub Check: Analyze (rust)
🔇 Additional comments (6)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/train.py (1)
816-825: LGTM: LoRA kwargs correctly assembled and passed.The dict comprehension properly filters
Nonevalues before passing hyperparameters toEvo2LoRA. This ensures only explicitly set CLI options are forwarded.sub-packages/bionemo-evo2/tests/bionemo/evo2/run/test_predict.py (2)
34-37: LGTM: Imports correctly added for new test utilities.The imports for
run_command_in_subprocessand the test command helpers (predict_cmd,small_training_finetune_cmd) are properly added to support the new LoRA prediction test.
372-452: LGTM: Comprehensive PEFT comparison test.The test validates end-to-end LoRA integration by:
- Fine-tuning a base model with LoRA
- Running predictions with and without the LoRA checkpoint
- Verifying that logits differ while maintaining consistent shapes and sequence indices
The test correctly asserts:
- Base model restoration without adapters during fine-tuning (lines 400-401)
- Adapter loading during PEFT prediction (lines 434-435)
- Result divergence between base and PEFT predictions (line 449)
- Shape consistency (lines 450-452)
sub-packages/bionemo-evo2/src/bionemo/evo2/run/predict.py (3)
44-44: LGTM: Evo2LoRA import correctly updated.The import path for
Evo2LoRAis now correct (bionemo.evo2.models.peft), aligning with the module's actual location.
539-539: LGTM: Centralized callbacks list.The callbacks are now properly managed in a centralized list and passed to the Trainer, improving maintainability.
599-599: LGTM: LoRA checkpoint path correctly forwarded.The
lora_checkpoint_pathargument is properly passed from CLI args to thepredict()function.
014de80 to
81680c9
Compare
Signed-off-by: Bruno Alvisio <balvisio@nvidia.com>
81680c9 to
5896940
Compare
|
/ok to test 5896940 |
Description
Adds support for Evo2 predict using a LoRA checkpoint
Type of changes
CI Pipeline Configuration
Configure CI behavior by applying the relevant labels:
Note
By default, the notebooks validation tests are skipped unless explicitly enabled.
Authorizing CI Runs
We use copy-pr-bot to manage authorization of CI
runs on NVIDIA's compute resources.
automatically be copied to a pull-request/ prefixed branch in the source repository (e.g. pull-request/123)
/ok to testcomment on the pull request to trigger CI. This will need to be done for each new commit.Usage
# TODO: Add code snippetPre-submit Checklist
Summary by CodeRabbit
New Features
Tests